import argparse
import sys
import gitlab
import os
import git
from urllib.parse import urlparse, urlunsplit
from cvek import command
from cvek.command import Commands
from cvek.config import Config, ConfigFileError
from .logging import log
import requests


def cd_to_root_of_git_project():
    os.chdir(git.Repo(search_parent_directories=True).working_tree_dir)


def git_remote_url():
    return git.Repo(search_parent_directories=True).remote().url


def parse_git_remote_url(remote_url):
    """
    解析 GitLab SSH URL 并返回 GitLab 实例地址和项目路径。

    示例:
    >>> parse_git_remote_url('git@192.168.10.152:cyos-security/public/containerd.git')
    ('https://192.168.10.152', 'cyos-security/public/containerd')
    """
    if not remote_url.startswith("git@"):
        raise ValueError("Unsupported remote URL format. Expected git@ SSH URL.")

    # 将 git@ SSH URL 转换为伪 HTTPS URL 以便使用 URL 解析库
    pseudo_url = remote_url.replace(":", "/").replace("git@", "http://", 1)
    parsed = urlparse(pseudo_url)

    # 重新构造 GitLab 实例地址
    gitlab_url = urlunsplit((parsed.scheme, parsed.netloc, "", "", ""))

    # 提取项目路径并去除 .git 后缀
    project_path = "/".join(parsed.path.split("/")[1:]).replace(".git", "")

    return gitlab_url, project_path


class CVEK:
    def __init__(self):
        # cd_to_root_of_git_project()
        self._gl = None
        self._host = None
        self._project_path = None

        self.config = Config()

        self.parser = argparse.ArgumentParser()
        self.parser.add_argument("--debug", "-v", action="store_true", default=False)
        subparsers = self.parser.add_subparsers(required=True)
        self.subparsers = subparsers

        download_parser = subparsers.add_parser(
            "download", help="Download issues from Gitlab"
        )
        download_parser.set_defaults(command=self.download_issues)

        upload_parser = subparsers.add_parser("upload", help="Upload issues to Gitlab")
        upload_parser.set_defaults(command=self.update_issues)

        clone_parser = subparsers.add_parser(
            "clone", help="Clone a project from GitLab"
        )
        clone_parser.add_argument("project_name", help="Name of the project to clone")
        clone_parser.set_defaults(command=self.clone_project)

        generate_parser = subparsers.add_parser(
            "generate", help="Generate issues from public CVEs information"
        )
        generate_parser.add_argument(
            "--cve-id",
            type=str,
            help="CVE ID to generate issues for",
            required=True,
        )
        # generate_parser.add_argument(
        #     "--package-name",
        #     type=str,
        #     help="Package name to generate issues for",
        #     required=True,
        # )
        generate_parser.set_defaults(command=self.generate_issue)

        self.register_verrel()
        self.register_config()

        self.cmd = Commands(self.config)

    def register_verrel(self):
        verrel_parser = self.subparsers.add_parser(
            "verrel", help="obtain last changelog entry to a verrel file"
        )
        verrel_parser.set_defaults(command=self.verrel)

    def register_config(self):
        config_parser = self.subparsers.add_parser(
            "config", help="manage cvek configuration"
        )
        config_parser.add_argument("name", help="Name of the config option to set")
        config_parser.add_argument("value", help="Value to set for the config option")
        config_parser.set_defaults(command=self.cmd_config)

    def parse_args(self):
        self.args = self.parser.parse_args()
        return self.args

    def run_command(self):
        self.args.command()

    @property
    def gl(self):
        if not self._gl:
            self.connect_gitlab()
        return self._gl

    def connect_gitlab(self):
        try:
            self._gl = gitlab.Gitlab(self.host, private_token=self.config.gitlab_token)
            self._gl.auth()
        except ConfigFileError:
            log.error(
                "Please set the PRIVATE_TOKEN in the config file. "
                "You can use `cvek config PRIVATE_TOKEN <your token>` to set it."
            )
            raise

    @property
    def host(self):
        if not self._host:
            self._host, self._project_path = parse_git_remote_url(git_remote_url())
        return self._host

    @property
    def project_path(self):
        if not self._project_path:
            self._host, self._project_path = parse_git_remote_url(git_remote_url())
        return self._project_path

    def get_current_user(self, token=None):
        headers = {
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        }
        
        try:
            response = requests.get(
                f"http://192.168.10.152/api/v4/user",
                headers=headers,
                timeout=10
            )
            response.raise_for_status()
            user_data = response.json()
            return user_data["username"]
        except requests.exceptions.RequestException as e:
            error_msg = [
                f"API调用失败: {str(e)}",
                "提示: 您可以在这里生成个人访问令牌",
                "地址: http://192.168.10.152/-/profile/personal_access_tokens",
                "所需的最小权限范围是 'api' 和 'write_repository'。"
            ]
            raise Exception("\n".join(error_msg)) from e
       
    def cmd_config(self):
        if self.args.name != "PRIVATE_TOKEN":
            print("请运行 \"cvek config PRIVATE_TOKEN <token>\" 命令来设置您的私有令牌")
            return

        current_token = self.config.gitlab_token
        if current_token:
            current_user = self.get_current_user(current_token)
            if current_user:
                response = input(f"您已以 {current_user} 身份登录，是否要重新认证? [y/n] ").strip().lower()
                if response not in ("y", "yes"):
                    print("保持现有登录状态")
                    return
        
        new_token = self.args.value
        username = self.get_current_user(new_token)
        if username:
            self.config[self.args.name] = self.args.value
            self.config.save()
            print(f"成功登录 {username} 用户")

    def download_issues(self):
        """Download all issues from a specified project and save them as Markdown files."""
        project = self.gl.projects.get(self.project_path)
        issues = project.issues.list(get_all=True)

        # 创建 cve 目录
        os.makedirs("cve", exist_ok=True)

        # 保存每个 issue 描述到文件
        for issue in issues:
            issue_number = issue.iid
            issue_title = issue.title
            description = issue.description
            tags = issue.labels
            assignee = issue.assignee if hasattr(issue, "assignee") else None

            # Sanitize the title to be used as a filename
            safe_title = issue_title.replace(" ", "_").replace("/", "_")
            filename = f"cve/{safe_title}.md"

            try:
                with open(filename, "w", encoding="utf-8") as file:
                    file.write(f"---\nissue_number: {issue_number}\n")
                    file.write(f"tags: {', '.join(tags) if tags else ''}\n")
                    file.write(
                        f"assignee: {assignee['username'] if assignee else ''}\n---\n\n"
                    )
                    file.write(description + "\n")
            except Exception as e:
                log.error(f"Failed to write file {filename}: {e}")
                raise

        log.info(f"Saved {len(issues)} issues to the 'cve' directory.")

    def update_issues(self):
        """Update issues in a specified project with the latest descriptions."""
        # 获取项目对象
        try:
            project = self.gl.projects.get(self.project_path)
        except Exception as e:
            print(f"Failed to get project: {e}")
            exit(1)

        # 检查 cve 目录是否存在
        if not os.path.exists("cve"):
            print("Error: cve directory not found.")
            exit(1)

        # 遍历 cve 目录下的所有以 CVE 开头的 markdown 文件
        for filename in os.listdir("cve"):
            if filename.startswith("CVE") and filename.endswith(".md"):
                file_path = os.path.join("cve", filename)

                # 读取文件内容
                try:
                    with open(file_path, "r", encoding="utf-8") as file:
                        content = file.read()
                except Exception as e:
                    print(f"Failed to read file {file_path}: {e}")
                    continue

                # 解析 YAML metadata
                try:
                    metadata_end = content.find("---", 3)
                    if metadata_end != -1:
                        metadata = content[3:metadata_end].strip()
                        content_lines = metadata.split("\n")

                        issue_number = None
                        assignee = None
                        tags = []

                        for line in content_lines:
                            if line.startswith("issue_number:"):
                                issue_number = int(line.split(":", 1)[1].strip())
                            elif line.startswith("assignee:"):
                                assignee = line.split(":", 1)[1].strip()
                            elif line.startswith("tags:"):
                                tags = [
                                    tag.strip()
                                    for tag in line.split(":", 1)[1].strip().split(",")
                                    if tag.strip()
                                ]

                        if issue_number is None:
                            print(f"No issue_number found in {file_path}")
                            continue

                        # 获取 issue
                        issue = project.issues.get(issue_number)

                        # 更新 issue 描述
                        issue.description = content[metadata_end + 3 :].strip()

                        # 更新 labels
                        issue.labels = tags

                        # 更新 assignee
                        if assignee:
                            users = self.gl.users.list(username=assignee)
                            if users:
                                issue.assignee_ids = [users[0].id]
                            else:
                                print(f"Assignee {assignee} not found in GitLab")

                        # 保存更新
                        issue.save()

                except Exception as e:
                    print(f"Failed to process file {file_path}: {e}")

        print("Issues updated successfully.")

    def generate_issue(self):
        """Generate issues in a specified project based on the cve id and package name."""
        self.cmd.generate_issue(self.args.cve_id)

    def clone_project(self):
        """Clone a GitLab project"""
        project_name = self.args.project_name
        gitlab_url = "http://192.168.10.152"

        try:
            gl = gitlab.Gitlab(gitlab_url, private_token=self.private_token)
            gl.auth()
        except Exception as e:
            print(f"❌ Failed to connect to GitLab: {e}")
            exit(1)

        print(f"🔍 Searching for project '{project_name}' ...")

        projects = gl.projects.list(search=project_name)
        project = None
        for project in projects:
            if "trash" in project.web_url or "toolkits" in project.web_url:
                continue
            if project.name == project_name and "cyos-security" in project.web_url:
                break

        if not project or project.name != project_name:
            print(f"❌ Project '{project_name}' not found.")
            exit(1)

        clone_url = project.ssh_url_to_repo
        print(f"✅ Found: {project.path_with_namespace}")
        print(f"Cloning from: {clone_url}")

        try:
            git.Repo.clone_from(clone_url, os.path.join(os.getcwd(), project_name))
            print(f"✅ Project '{project_name}' cloned successfully.")
            self.update_gitignore(project_name)
        except Exception as e:
            print(f"❌ Failed to clone: {e}")

    def update_gitignore(self, project_name):
        project_dir = os.path.join(os.getcwd(), project_name)
        gitignore_path = os.path.join(project_dir, ".gitignore")

        if not os.path.exists(gitignore_path):
            return

        try:
            with open(gitignore_path, "r+", encoding="utf-8") as f:
                lines = f.read().splitlines()
                if "cve/" not in lines:
                    f.write("\ncve\n")
                    print("✅ Added 'cve' to existing .gitignore.")
                else:
                    print("ℹ️  'cve' already exists in .gitignore.")
        except Exception as e:
            print(f"❌ Failed to update .gitignore: {e}")

    def verrel(self):
        print(self.cmd.version_release)
        print(self.cmd.rpm_upstream_branch)


def main():
    cli = CVEK()
    cli.parse_args()
    try:
        cli.run_command()
    except KeyboardInterrupt:
        pass
    except Exception as e:
        log.error(f"Could not execute {cli.args.command.__name__}: {e}")
        if cli.args.debug:
            raise
        sys.exit(1)


if __name__ == "__main__":
    main()
